<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<button>按钮</button>
<script>
    /*
    * 封装节流函数：（均匀间隔执行,类似定时开门放人）
    *   throttle
    *   fn 原始函数 作为参数
    *   let f = throttle(fn,delay)
    *   f 不停执行
    *   函数f内部 对应的 参数fn函数 -> 均匀间隔执行
    */

    let button = document.querySelector("button")
    console.log("button")

    function throttle(fn,delay){ //节流函数接收两个参数：回调函数fn 延迟时间delay
        let flag = true //记录状态，为true允许执行内部函数，false不允许执行内部函数

        //节流函数的执行结果就是返回一个内部函数
        return function(){
            //想要运行内部函数之前，flag分为两种状态情况：一种是默认状态true，一种是延迟时间内的false状态

            //这里是flag为false的情况，函数就走这个if判断
            if(!flag) return console.log("大招还在cd中")//如果flag为false，直接退出，等到flag变成true之后，才有被执行的可能

            //这里是flag为true的情况，函数就不走上面的那个if判断，直接走这一步
            //为了防止在这一次执行内部函数时又被另起一次调用执行内部函数，需要手动将状态改为false，执行完这一次之后过一段时间再改回true
            flag = false //因为这里是默认flag为true的情况，需要手动改为false，类似于先把门关上，过一阵子再开门放人
            setTimeout(() => flag = true,delay) //设置一个延时定时器，定时器只需要做一件事情：在delay时间之后将状态重新改成true
            fn() //设置定时器的同时也去执行接收的回调函数fn
        }
    }
    /*节流函数执行过程详解：
    * 1.节流函数throttle，接收两个参数，一个是需要被执行的回调函数fn(fn不会无cd执行，每次需要间隔一段延迟时间delay)，一个是延迟时间delay
    * 2.在执行回调函数fn之前，不会立马执行fn，会先进行状态判断(默认设置一个flag状态为true)，如果flag为false，直接return退出函数，
    *   等到flag状态重新变为true，fn才有再次被执行的可能，
    * 3.如果flag状态为true，可以执行fn，但是与此同时需要手动将flag状态改为false(进入cd)，再设置一个延时定时器，
    *   延时定时器内规定delay时间过后，flag状态改回true，这样才能在下一次再次执行回调函数fn
    *
    * */

    function run(){ //需要传入节流函数的回调函数
        console.log("我放大招了")
    }

    let f = throttle(run,5000) //将节流函数执行的返回结果绑定给f f就等同于节流函数执行后返回的内部的函数

    button.onclick = f



    /* 封装防抖函数：（函数f执行完成停止一段时间，参数fn函数正真执行）
    *   debounce
    *   fn原始函数
    *   let f = debounce(fn,delay)
    *   f 不停执行
    *   等函数f执行完成了之后 函数f内部 对应的 参数fn函数 -> 隔了一秒才执行
    *
    * */
    //频繁地进行各种操作，以最后一次的操作为准去执行
    function debounce(fn,delay){ //防抖函数debounce接收两个参数，一个是需要被执行的回调函数fn，一个是延迟时间delay
        let timer = null //用闭包的知识，用timer来保存每一次生成的延时定时器，初始值默认为null

        //最后也是返回一个内部函数
        return function (){
            //每一次执行前先进行排他操作，将上一次生成的延时定时器清除
            clearTimeout(timer)

            //这一次执行是生成一个全新的定时器，绑定到timer上
            timer = setTimeout(fn,delay) //定时器中接收两个参数，一个是需要执行的回调函数fn，一个是具体延迟时间
        }
    }

    function say(){
        console.log("发送请求")
    }

    d = debounce(say,2000)

    button.onclick = d

    let utils = { //模块化，用一个工具包对象来保存这两个封装好的函数
        debounce,
        throttle
    }

    //以后每次需要使用就 utils.throttle(fn,delay)

</script>
</body>
</html>